﻿// =====================================================================
//  This file is part of the Microsoft Dynamics CRM SDK code samples.
//
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
// =====================================================================

//<snippetCrmServiceHelper>
using System;
using System.Collections.Generic;
using System.ServiceModel.Description;

// These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly
// located in the SDK\bin folder of the SDK download.
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;
using System.Text;

namespace TransactionServer.CRMActivities
{
    public enum AuthenticationType { AD, Passport, SPLA };

    /// <summary>
    /// Provides server connection information.
    /// </summary>
    public class ServerConnection
    {
        #region Inner classes
        /// <summary>
        /// Stores CRM server configuration information.
        /// </summary>
        public class Configuration
        {
            public String ServerAddress;
            public String OrganizationName;
            public Uri DiscoveryUri;
            public Uri OrganizationUri;
            public Uri HomeRealmUri = null;
            public ClientCredentials DeviceCredentials = null;
            public ClientCredentials Credentials = null;
            public AuthenticationType EndpointType;
        }
        #endregion Inner classes

        #region Public properties

        public List<Configuration> configurations = null;

        #endregion Public properties

        #region Private properties

        private Configuration config = new Configuration();

        #endregion Private properties

        #region Public methods
        /// <summary>
        /// Obtains the server connection information including the target organization's
        /// Uri and user login credentials from the user.
        /// </summary>
        public virtual Configuration GetServerConfiguration(Dictionary<string, string> settings)
        {
            bool ssl;

            // Get the server address. If no value is entered, default to Microsoft Dynamics
            // CRM Online in the North American data center.
            config.ServerAddress = settings["url"];

            // One of the Microsoft Dynamics CRM Online data centers.
            if (config.ServerAddress.EndsWith(".dynamics.com"))
            {
                config.DiscoveryUri =
                    new Uri(String.Format("https://dev.{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));

                // Set or get the device credentials. Required for Windows Live ID authentication. 
                //config.DeviceCredentials = GetDeviceCredentials();
            }

            // Does the server use Secure Socket Layer (https)?
            else if (settings["ssl"] == "true")
                config.DiscoveryUri =
                    new Uri(String.Format("https://{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));
            else
                config.DiscoveryUri =
                    new Uri(String.Format("http://{0}/XRMServices/2011/Discovery.svc", config.ServerAddress));

            // Get the user's logon credentials.
            config.Credentials = GetUserLogonCredentials(settings);

            // Get the target organization.
            config.OrganizationUri = GetOrganizationAddress(config.DiscoveryUri, settings);

            //Store the completed configuration.
            if (configurations == null) configurations = new List<Configuration>();
            configurations.Add(config);

            return config;
        }

        /// <summary>
        /// Discovers the organizations that the calling user belongs to.
        /// </summary>
        /// <param name="service">A Discovery service proxy instance.</param>
        /// <returns>Array containing detailed information on each organization that 
        /// the user belongs to.</returns>
        public OrganizationDetailCollection DiscoverOrganizations(IDiscoveryService service)
        {
            RetrieveOrganizationsRequest orgRequest = new RetrieveOrganizationsRequest();
            RetrieveOrganizationsResponse orgResponse =
                (RetrieveOrganizationsResponse)service.Execute(orgRequest);

            return orgResponse.Details;
        }

        /// <summary>
        /// Finds a specific organization detail in the array of organization details
        /// returned from the Discovery service.
        /// </summary>
        /// <param name=orgFriendlyName">The friendly name of the organization to find.</param>
        /// <param name="orgDetails">Array of organization detail object returned from the discovery service.</param>
        /// <returns>Organization details or null if the organization was not found.</returns>
        /// <seealso cref="DiscoveryOrganizations"/>
        public OrganizationDetail FindOrganization(string orgFriendlyName, OrganizationDetail[] orgDetails)
        {
            OrganizationDetail orgDetail = null;

            foreach (OrganizationDetail detail in orgDetails)
            {
                if (String.Compare(detail.FriendlyName, orgFriendlyName) == 0)
                {
                    orgDetail = detail;
                    break;
                }
            }
            return orgDetail;
        }

        /// <summary>
        /// Reads a server configuration file.
        /// </summary>
        /// <param name="pathname">The file system path to the server configuration file.</param>
        /// <remarks>Server configurations are appended to the public configurations list.</remarks>
        public void ReadConfigurations(String pathname)
        {
            //if (configurations == null)
            //    configurations = new List<Configuration>();
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// Writes all server configurations to a file.
        /// </summary>
        /// <remarks>If the file exists, it is overwritten.</remarks>
        /// <param name="pathname">The file name and system path of the output configuration file.</param>
        public void SaveConfigurations(String pathname)
        {
            if (configurations == null)
                throw new Exception("No server connection configurations were found.");

            //TODO: Delete file if it exists.
            // Delete file here.

            foreach (Configuration config in configurations)
                SaveConfiguration(pathname, config, true);
        }

        /// <summary>
        /// Writes a server configuration to a file.
        /// </summary>
        /// <param name="pathname">The file name and system path of the output configuration file.</param>
        /// <param name="config">A server connection configuration.</param>
        /// <param name="append">If true, the configuration is appended to the file, otherwise a new file
        /// is created.</param>
        public void SaveConfiguration(String pathname, Configuration config, bool append)
        {
            throw new System.NotImplementedException();
        }

        /// <summary>
        /// Obtains the authentication type of the CRM server.
        /// </summary>
        /// <param name="uri">Uri of the CRM Discovery service.</param>
        /// <returns>Authentication type.</returns>
        public AuthenticationProviderType GetServerType(Uri uri)
        {
            return ServiceConfigurationFactory.CreateConfiguration<IDiscoveryService>(uri).AuthenticationType;
        }
        #endregion Public methods

        #region Protected methods

        /// <summary>
        /// Obtains the name and port of the server running the Microsoft Dynamics CRM
        /// Discovery service.
        /// </summary>
        /// <returns>The server's network name and optional TCP/IP port.</returns>
        protected virtual String GetServerAddress(out bool ssl)
        {
            ssl = false;

            Console.Write("Enter a CRM server name and port [crm.dynamics.com]: ");
            String server = Console.ReadLine();

            if (server.EndsWith(".dynamics.com") || String.IsNullOrWhiteSpace(server))
            {
                ssl = true;
            }
            else
            {
                Console.Write("Is this server configured for Secure Socket Layer (https) (y/n) [n]: ");
                String answer = Console.ReadLine();

                if (answer == "y" || answer == "Y")
                    ssl = true;
            }

            return server;
        }

        /// <summary>
        /// Obtains the Web address (Uri) of the target organization.
        /// </summary>
        /// <param name="discoveryServiceUri">The Uri of the CRM Discovery service.</param>
        /// <returns>Uri of the organization service or an empty string.</returns>
        protected virtual Uri GetOrganizationAddress(Uri discoveryServiceUri, Dictionary<string, string> settings)
        {
            using (DiscoveryServiceProxy serviceProxy = new DiscoveryServiceProxy(discoveryServiceUri, null, config.Credentials, config.DeviceCredentials))
            {
                // Obtain organization information from the Discovery service. 
                if (serviceProxy != null)
                {                    
                    // Obtain information about the organizations that the system user belongs to.
                    OrganizationDetailCollection orgs = DiscoverOrganizations(serviceProxy);
                    System.Uri result = null;

                    foreach (var org in orgs)
                    {
                        if (org.UniqueName == settings["organisation"])
                        {
                            result = new System.Uri(org.Endpoints[EndpointType.OrganizationService]);
                        }
                    }

                    if (result != null)
                    {
                        return result;
                    }
                    else
                        throw new Exception("The specified organization does not exist.");
                }

                else
                    throw new Exception("An invalid server name was specified.");
            }
        }

        /// <summary>
        /// Obtains the user's logon credentials for the target server.
        /// </summary>
        /// <returns>Logon credentials of the user.</returns>
        protected virtual ClientCredentials GetUserLogonCredentials(Dictionary<string, string> settings)
        {
            ClientCredentials credentials = new ClientCredentials(); ;
            String userName;
            String password;
            String domain;

            // An on-premises Microsoft Dynamics CRM server deployment. 
            if (GetServerType(config.DiscoveryUri) == AuthenticationProviderType.ActiveDirectory)
            {
                String[] domainAndUserName;

                do
                {

                    domainAndUserName = new string[] { settings["domain"], settings["user"] };

                    if (domainAndUserName.Length == 1 && String.IsNullOrWhiteSpace(domainAndUserName[0]))
                    {
                        return null;
                    }
                }
                while (domainAndUserName.Length != 2 || String.IsNullOrWhiteSpace(domainAndUserName[0]) || String.IsNullOrWhiteSpace(domainAndUserName[1]));

                domain = domainAndUserName[0];
                userName = domainAndUserName[1];

                password = settings["password"];

                credentials.Windows.ClientCredential = new System.Net.NetworkCredential(userName, password, domain);
            }
            // An Microsoft Dynamics CRM Online server deployment. 
            else if (GetServerType(config.DiscoveryUri) == AuthenticationProviderType.LiveId)
            {
                userName = settings["user"];
                if (string.IsNullOrWhiteSpace(userName))
                {
                    return null;
                }

                password = settings["password"];

                credentials.UserName.UserName = userName;
                credentials.UserName.Password = password;
            }
            // An internet facing (IFD) Microsoft Dynamics CRM server deployment.  
            else if (GetServerType(config.DiscoveryUri) == AuthenticationProviderType.Federation)
            {
                userName = settings["user"];
                if (string.IsNullOrWhiteSpace(userName))
                {
                    return null;
                }

                password = settings["password"];

                credentials.UserName.UserName = userName;
                credentials.UserName.Password = password;
            }
            else
                return null;

            return credentials;
        }
        protected string ReadPassword()
        {
            StringBuilder sbPassword = new StringBuilder();

            ConsoleKeyInfo info = Console.ReadKey(true);
            while (info.Key != ConsoleKey.Enter)
            {
                if (info.Key == ConsoleKey.Backspace)
                {
                    if (sbPassword.Length != 0)
                    {
                        sbPassword.Remove(sbPassword.Length - 1, 1);
                        Console.Write("\b \b");     // erase last char
                    }
                }
                else if (info.KeyChar >= ' ')           // no control chars
                {
                    sbPassword.Append(info.KeyChar);
                    Console.Write("*");
                }
                info = Console.ReadKey(true);
            }

            Console.WriteLine();

            return sbPassword.ToString();
        }
        #endregion Private methods
    }

    #region Internal Classes

    //<snippetCrmServiceHelper1>
    /// <summary>
    /// The SolutionComponentType defines the type of solution component.
    /// </summary>
    public static class SolutionComponentType
    {
        public const int Attachment = 35;
        public const int Attribute = 2;
        public const int AttributeLookupValue = 5;
        public const int AttributeMap = 47;
        public const int AttributePicklistValue = 4;
        public const int ConnectionRole = 63;
        public const int ContractTemplate = 37;
        public const int DisplayString = 22;
        public const int DisplayStringMap = 23;
        public const int DuplicateRule = 44;
        public const int DuplicateRuleCondition = 45;
        public const int EmailTemplate = 36;
        public const int Entity = 1;
        public const int EntityMap = 46;
        public const int EntityRelationship = 10;
        public const int EntityRelationshipRelationships = 12;
        public const int EntityRelationshipRole = 11;
        public const int FieldPermission = 71;
        public const int FieldSecurityProfile = 70;
        public const int Form = 24;
        public const int KBArticleTemplate = 38;
        public const int LocalizedLabel = 7;
        public const int MailMergeTemplate = 39;
        public const int ManagedProperty = 13;
        public const int OptionSet = 9;
        public const int Organization = 25;
        public const int PluginAssembly = 91;
        public const int PluginType = 90;
        public const int Relationship = 3;
        public const int RelationshipExtraCondition = 8;
        public const int Report = 31;
        public const int ReportCategory = 33;
        public const int ReportEntity = 32;
        public const int ReportVisibility = 34;
        public const int RibbonCommand = 48;
        public const int RibbonContextGroup = 49;
        public const int RibbonCustomization = 50;
        public const int RibbonDiff = 55;
        public const int RibbonRule = 52;
        public const int RibbonTabToCommandMap = 53;
        public const int Role = 20;
        public const int RolePrivilege = 21;
        public const int SavedQuery = 26;
        public const int SavedQueryVisualization = 59;
        public const int SDKMessageProcessingStep = 92;
        public const int SDKMessageProcessingStepImage = 93;
        public const int SDKMessageProcessingStepSecureConfig = 94;
        public const int ServiceEndpoint = 95;
        public const int SiteMap = 62;
        public const int SystemForm = 60;
        public const int ViewAttribute = 6;
        public const int WebResource = 61;
        public const int Workflow = 29;
    }
    //</snippetCrmServiceHelper1>
    #endregion
}
//</snippetCrmServiceHelper>
